<?php

namespace cosocket\system;

class Debug
{

    private static $DEBUG_TRACE = false;

    public static function getMicroTime()
    {
        list($usec, $sec) = \explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    private static function convert($size)
    {
        $unit = array('B', 'K', 'M', 'G', 'T', 'P');
        return round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
    }


    /**
     * Send print to terminal.
     */
    private static function _log($msgType, $args)
    {

        if (count($args) == 1) {
            $msg = is_scalar($args[0]) ? $args[0] : self::dump($args[0]);
        } else {
            $msg = self::dump($args);
        }

        if (self::$DEBUG_TRACE) {
            $trace = self::getTrace();
        } else {
            $trace = array();
        }
        if ($msgType == 'debug') {
            Terminal::drawStr($msg, 'magenta');
        } else if ($msgType == 'error') {
            Terminal::drawStr($msg, 'red');
        } else if ($msgType == 'info') {
            Terminal::drawStr($msg, 'brown');
        } else {
            Terminal::drawStr($msg, 'default');
        }
        //echo "\n";
        !empty($trace) && Terminal::drawStr("\t" . implode(" <-- ", $trace) . "\n");
    }

    private static function getTrace()
    {
        $traces = debug_backtrace();
        // only display 2 to 6 backtrace
        for ($i = 2, $n = count($traces); $i < $n && $i < 7; $i++) {
            //for ($i = 3, $n = count($traces); $i < $n; $i++){
            $trace = $traces[$i];
            if (isset($trace['type'])) {
                $callInfo = $trace['class'] . $trace['type'] . $trace['function'] . '()';
            } else {
                $callInfo = 'internal:' . $trace['function'] . '()';
            }
            if (isset($trace['file'])) {
                $fileInfo = str_replace('/', '', $trace['file']) . ':' . $trace['line'];
            } else {
                $fileInfo = '';
            }
            //$traces_data[] = $fileInfo . " " . $callInfo;
            $traces_data[] = $callInfo . " " . $fileInfo;
        }
        return $traces_data;
    }

    private static function tracefile($msgType){
        $trace=debug_backtrace();
        $caller0=$trace[1];
        $caller=$trace[2];

        if(isset($caller['class'])){
            $c = $caller['class'];
            $f=$caller['function'];
            $file = $caller0['file'];

            $l=$caller0['line'];

            if ($msgType == 'debug') {
                Terminal::drawStr(PHP_EOL."dump from: class $c ($file): in function '$f' line $l".PHP_EOL, 'magenta');
            } else if ($msgType == 'error') {
                Terminal::drawStr(PHP_EOL."dump from: class $c ($file): in function '$f' line $l".PHP_EOL, 'red');
            } else if ($msgType == 'info') {
                Terminal::drawStr(PHP_EOL."dump from: class $c ($file): in function '$f' line $l".PHP_EOL, 'brown');
            } else {
                Terminal::drawStr(PHP_EOL."dump from: class $c ($file): in function '$f' line $l".PHP_EOL, 'default');
            }
        }else{
            $f=$caller['function'];
            $l=$caller['line'];
            $file = $caller0['file'];
            $l=$caller0['line'];
            Terminal::drawStr(PHP_EOL."dump from ($file): in function '$f' in line $l".PHP_EOL);
        }
    }

    private static function dump()
    {
        ob_start();

        foreach (func_get_args() as $v) {
            var_dump($v);
        }

        $dump = ob_get_contents();
        ob_end_clean();

        return $dump;
    }

    public static function log()
    {
        self::tracefile('log');
        self::_log('log', func_get_args());
    }

    public static function info()
    {
        self::tracefile('info');
        self::_log('info', func_get_args());
    }

    public static function debug()
    {
        self::tracefile('debug');
        self::_log('debug', func_get_args());
    }

    public static function error()
    {
        self::tracefile('error');
        self::_log('error', func_get_args());
    }

}

class Terminal
{
    /*
    * color define
    */
    private static $color = array(
        'default' => 0,
        'black' => 30,
        'red' => 31,
        'green' => 32,
        'brown' => 33,
        'blue' => 34,
        'magenta' => 35,
        'cyan' => 36,
        'lightgray' => 37,
    );

    /**
     * Is color allowed in terminal?
     * @var boolean
     */
    public static $enable_color = true;

    /**
     * Maximum terminal width
     * @var int
     */
    private static $columns = 80;

    public static function drawStr($str, $color = 'default')
    {
        self::setStyle(isset(self::$color[$color]) ? self::$color[$color] : $color);
        echo $str;
        self::resetStyle();
    }

    /**
     * Read a line from STDIN
     * @return string Line
     */
    public static function readln()
    {
        return fgets(STDIN);
    }

    /**
     * Clear the terminal with CLR
     * @return void
     */
    public static function clearScreen()
    {
        echo "\x0c";
    }

    /**
     * Set text style
     * @param string Style
     * @return void
     */
    public static function setStyle($c)
    {
        if (self::$enable_color) {
            echo "\033[" . $c . 'm';
        }
    }

    /**
     * Reset style to default
     * @return void
     */
    public static function resetStyle()
    {
        if (self::$enable_color) {
            echo "\033[0m";
        }
    }

    /**
     * Counting terminal char width
     * @return int
     */
    private function getMaxColumns()
    {
        if (
            preg_match_all("/columns.([0-9]+);/", strtolower(@exec('stty -a | grep columns')), $output)
            && 2 == sizeof($output)
        ) {
            return $output[1][0];
        }

        return 80;
    }

    /**
     * Draw param (like in man)
     * @param string Param name
     * @param string Param description
     * @param array Param allowed values
     * @return void
     */
    public static function drawParam($name, $description, $values = '')
    {
        self::$columns = self::getMaxColumns();
        $paramw = round(self::$columns / 3);

        echo "\n";

        $leftcolumn = array();

        $valstr = is_array($values) ? implode('|', array_keys($values)) : $values;

        if ('' !== $valstr) {
            $valstr = '=[' . $valstr . ']';
        }

        $paramstr = "  \033[1m--" . $name . $valstr . "\033[0m";

        $pl = strlen($paramstr);
        if ($pl + 2 >= $paramw) {
            $paramw = $pl + 3;
        }

        $descw = self::$columns - $paramw;

        $leftcolumn[] = $paramstr;

        if (is_array($values)) {
            foreach ($values as $key => $value) {
                $leftcolumn[] = '    ' . $key . ' - ' . $value;
            }
        }

        if (strlen($description) <= $descw) {
            $rightcolumn[] = $description;
        } else {
            $m = explode(' ', $description);

            $descstr = '';

            while (sizeof($m) > 0) {
                $el = array_shift($m);

                if (strlen($descstr) + strlen($el) >= $descw) {
                    $rightcolumn[] = $descstr;
                    $descstr = '';
                } else {
                    $descstr .= ' ';
                }

                $descstr .= $el;
            }

            if ('' !== $descstr) {
                $rightcolumn[] = $descstr;
            }
        }

        while (
            sizeof($leftcolumn) > 0
            || sizeof($rightcolumn) > 0
        ) {
            if ($l = array_shift($leftcolumn)) {
                echo str_pad($l, $paramw, ' ');
            } else {
                echo str_repeat(' ', $paramw - 7);
            }

            if ($r = array_shift($rightcolumn)) {
                echo $r;
            }

            echo "\n";
        }
    }

    /**
     * Draw a table
     * @param array Array of table's rows.
     * @return void
     */
    public static function drawTable($rows)
    {
        $pad = array();

        foreach ($rows as $row) {
            foreach ($row as $k => $v) {
                if (substr($k, 0, 1) == '_') {
                    continue;
                }

                if (
                    !isset($pad[$k])
                    || (strlen($v) > $pad[$k])
                ) {
                    $pad[$k] = strlen($v);
                }
            }
        }

        foreach ($rows as $row) {
            if (isset($row['_color'])) {
                self::setStyle($row['_color']);
            }

            if (isset($row['_bold'])) {
                self::setStyle('1');
            }

            if (isset($row['_'])) {
                echo $row['_'];
            } else {
                $i = 0;

                foreach ($row as $k => $v) {
                    if (substr($k, 0, 1) == '_') {
                        continue;
                    }

                    if ($i > 0) {
                        echo "\t";
                    }

                    echo str_pad($v, $pad[$k]);
                    ++$i;
                }
            }

            self::resetStyle();
            echo "\n";
        }
    }
}

